/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.io;

import com.android.dx.io.instructions.DecodedInstruction;
import com.android.dx.io.opcode.OpcodeInfo;
import com.android.dx.util.DexException;

/**
 * Walks through a block of code and calls visitor call backs.
 */
public final class CodeReader {

	private Visitor fallbackVisitor = null;
	private Visitor stringVisitor = null;
	private Visitor typeVisitor = null;
	private Visitor fieldVisitor = null;
	private Visitor methodVisitor = null;

	/**
	 * Sets {@code visitor} as the visitor for all instructions.
	 */
	public void setAllVisitors(Visitor visitor) {
		fallbackVisitor = visitor;
		stringVisitor = visitor;
		typeVisitor = visitor;
		fieldVisitor = visitor;
		methodVisitor = visitor;
	}

	/**
	 * Sets {@code visitor} as the visitor for all instructions not otherwise
	 * handled.
	 */
	public void setFallbackVisitor(Visitor visitor) {
		fallbackVisitor = visitor;
	}

	/**
	 * Sets {@code visitor} as the visitor for all string instructions.
	 */
	public void setStringVisitor(Visitor visitor) {
		stringVisitor = visitor;
	}

	/**
	 * Sets {@code visitor} as the visitor for all type instructions.
	 */
	public void setTypeVisitor(Visitor visitor) {
		typeVisitor = visitor;
	}

	/**
	 * Sets {@code visitor} as the visitor for all field instructions.
	 */
	public void setFieldVisitor(Visitor visitor) {
		fieldVisitor = visitor;
	}

	/**
	 * Sets {@code visitor} as the visitor for all method instructions.
	 */
	public void setMethodVisitor(Visitor visitor) {
		methodVisitor = visitor;
	}

	public void visitAll(DecodedInstruction[] decodedInstructions)
			throws DexException {
		int size = decodedInstructions.length;

		for (int i = 0; i < size; i++) {
			DecodedInstruction one = decodedInstructions[i];
			if (one == null) {
				continue;
			}

			callVisit(decodedInstructions, one);
		}
	}

	public void visitAll(short[] encodedInstructions) throws DexException {
		DecodedInstruction[] decodedInstructions = DecodedInstruction
				.decodeAll(encodedInstructions);
		visitAll(decodedInstructions);
	}

	private void callVisit(DecodedInstruction[] all, DecodedInstruction one) {
		Visitor visitor = null;

		switch (OpcodeInfo.getIndexType(one.getOpcode())) {
		case STRING_REF:
			visitor = stringVisitor;
			break;
		case TYPE_REF:
			visitor = typeVisitor;
			break;
		case FIELD_REF:
			visitor = fieldVisitor;
			break;
		case METHOD_REF:
			visitor = methodVisitor;
			break;
		}

		if (visitor == null) {
			visitor = fallbackVisitor;
		}

		if (visitor != null) {
			visitor.visit(all, one);
		}
	}

	public interface Visitor {

		void visit(DecodedInstruction[] all, DecodedInstruction one);
	}
}
